/** 
 * \file import_osm.c
 *       Filename:  import_osm.c
 * =====================================================================================
 * 
 *    Description:  Import OpenStreetMap XML file (OSM file) together with Nasa Shuttle
 *    				radar topographic mission data (HGT file) to create a standard 
 *    				GIS-format file (MIF/MID text file), which can be read by usual 
 *    				GIS softwares like ArcGis, MapInfo, Grass.
 * 
 *        Version:  1.0
 *        Created:  29/10/2009 16:17:27
 *       Revision:  none
 *       Compiler:  gcc
 * 
 *         Author:  François Hissel (fh), francois.hissel@m4x.org
 *        Company:  
 * 
 * =====================================================================================
 */

#include	<ctype.h>
#include	<math.h>
#include	<stdlib.h>
#include	<stdio.h>
#include	<string.h>
#include	<errno.h>
#include	<sysexits.h>
#include	<sys/types.h>
#include	<unistd.h>
#include	<fcntl.h>
#include	<getopt.h>
#include	<libxml/parser.h>
#include	<libxml/tree.h>
#include	"webrequest.h"
#include	"projections.h"
#include	"types.h"

#define	OSM_HOST	"api.openstreetmap.org"			//!< Name of server hosting OpenStreetMap project
#define OSM_EXPORT	"/api/0.6/map"	//!< Path of export page on web server
#define	OSM_PARAMS	"?bbox="			//!< Name of parameters to be sent with request on OpenStreetMap website
#define STRING_SIZE 0xff	//!< Size of buffers for string manipulations. In particular this constant is used to define the size of the buffer for the storing of the website address
#define	NODE_SUFFIX	"_node"			//!< Suffix of Nodes MIF/MID files
#define	WAY_SUFFIX	"_way"			//!< Suffix of Ways MIF/MID files
#define	MIF_SUFFIX	".mif"			//!< Extension of MIF files
#define	MID_SUFFIX	".mid"			//!< Extension of MID files
#define	PROJECTION_CODE	5			//!< Code of the projection to use when writing MIF/MID file

/**
 * \brief Import XML file describing a road network from OpenStreetMap
 *
 * This function connects to OpenStreetMap website and downloads an XML file describing a road network in the sector limited by the four parameters given in argument. The content of the file is saved on the file with the descriptor fd. The function returns the effective number of bytes downloaded from the website, which should also be the size of the resulting file. If an error occurred during reading, -1 is returned.
 * \param fd Descriptor of the already opened file in which the content shall be written
 * \param west Minimum longitude in degrees
 * \param east Maximum longitude in degrees
 * \param south Minimum latitude in degrees
 * \param north Maximum latitude in degrees
 * \return Number of bytes of the XML content downloaded from the website, excluding headers and other stuff
 */
unsigned long import_osm(int fd,double west,double east,double south,double north) {
	char path[STRING_SIZE];
	if (snprintf(path,STRING_SIZE,"%s%s%f,%f,%f,%f",OSM_EXPORT,OSM_PARAMS,west,south,east,north)>=STRING_SIZE) {
		fprintf(stderr,"Address too long : %s\n",path);
		return -1;
	}
	return download(fd,OSM_HOST,HTTP_PORT,path,HTTP_HEADERS);
}

/**
 * \brief Create and open two files for MIF/MID export
 *
 * This function should not be used alone. It is called by MIF/MID export functions to create and open two files with the names generated from the mi_file argument. The headers of the MIF files are automatically generated, thanks to argument descr. The file descriptors are written in the mif and mid arguments. If an error occurs, a message is displayed on stderr, the files are closed and all memory is freed, while the descriptors take a null value.
 * \param mi_file "Root name" of the MIF/MID file in which results will be written. See also export_mif_mid() for more details.
 * \param suffix Suffix to add at the end of the root name of the file, before the extension
 * \param descr Description table of the data, used to generate the MIF file headers
 * \param mif Pointer to the MIF file descriptor
 * \param mid Pointer to the MID file descriptor
 */
void create_mif_mid_files(char *mi_file,char *suffix,Description *descr,FILE **mif,FILE **mid) {
	int num=strlen(mi_file)+strlen(suffix)+strlen(MID_SUFFIX);
	char *filename=(char*)malloc(num+1);
	if (!filename) {
		fprintf(stderr,"Memory allocation error.\n");
		mif=mid=0;
		return;
	}
	strcpy(filename,mi_file);
	strcat(filename,suffix);
	strcat(filename,MID_SUFFIX);
	*mid=fopen(filename,"w");
	if (!*mid) {
		fprintf(stderr,"Error while trying to create output file %s, error code %i.\n",filename,errno);
		free(filename);
		mif=mid=0;
		return;
	}
	filename=(char*)realloc(filename,num+1);
	num=strlen(mi_file)+strlen(suffix)+strlen(MIF_SUFFIX);
	strcpy(filename,mi_file);
	strcat(filename,suffix);
	strcat(filename,MIF_SUFFIX);
	*mif=fopen(filename,"w");
	free(filename);
	if (!*mif) {
		fprintf(stderr,"Error while trying to create output file %s, error code %i.\n",filename,errno);
		free(filename);
		fclose(*mid);
		mif=mid=0;
		return;
	}
	fprintf(*mif,"Version 300\nCharset WindowsLatin1\nDelimiter \",\"\nCoordSys Earth Projection 1,0\nColumns %i\n    id char(20)\n",descr->number);
	int i=0;
	int j;
	int nbattr=descr->number;
	for (i=0;i<nbattr;++i) for (j=0;j<nbattr;++j) if (descr->names[j]->position==i) fprintf(*mif,"    %s char(40)\n",descr->names[j]->name);
	fprintf(*mif,"DATA\n");
}

/**
 * \brief Exports a list of nodes to a MIF/MID file
 *
 * This function saves a list of nodes from the memory to the disk on the file which name is given in argument.
 * \param mi_file "Root name" of the MIF/MID file in which results will be written. See also export_mif_mid() for more details.
 * \param nodes Array of nodes to write on the disk
 * \param size Number of nodes
 */
void save_nodes(char *mi_file,Node **nodes,unsigned long size) {
	if (size<=0) return;
	Description *descr=nodes[0]->description;
	FILE *fmif,*fmid;
	create_mif_mid_files(mi_file,NODE_SUFFIX,descr,&fmif,&fmid);
	if (!fmif || !fmid) return;
	int i,j;
	for (i=0;i<size;++i) {
		fprintf(fmif,"POINT %f %f\n",nodes[i]->x,nodes[i]->y);
		fprintf(fmid,"\"%li\"",nodes[i]->id);
		for (j=0;j<descr->number;++j) fprintf(fmid,",\"%s\"",(nodes[i]->attributes[j])?(nodes[i]->attributes[j]):"");
		fprintf(fmid,"\n");
	}
	fclose(fmid);
	fclose(fmif);
}

/**
 * \brief Exports a list of ways to a MIF/MID file
 *
 * This function saves a list of roads from the memory to the disk on the file which name is given in argument. The raw ways imported from OpenStreetMap are splitted when a crossroad is detected, that is when another way has a common end with the current one. This merging of ways allows faster pathfinding algorithms which use less memory.
 * \param mi_file "Root name" of the MIF/MID file in which results will be written. See also export_mif_mid() for more details.
 * \param ways List of ways to write on disk
 */
void save_ways(char *mi_file,Ways *ways) {
	if (!ways) return;
	Description *descr=ways->way->description;
	FILE *fmif,*fmid;
	create_mif_mid_files(mi_file,WAY_SUFFIX,descr,&fmif,&fmid);
	if (!fmif || !fmid) return;
	Ways *cur=ways;
	int i,j;
	unsigned long id=0;
	while (cur!=0) if (cur->way->nb_nodes>1) {
		Nodes *n=cur->way->nodes;
		Nodes *cross;
		while (n!=0) {
			i=2;
			cross=n->next;	// Search next crossroads in the list of ways
			while (cross!=0 && cross->node->number<=1) {++i;cross=cross->next;}
			if (!cross) --i;
			if (i>1) {	// In this case, it means that cross=0, that is n is the last node. This may only happen when the last node is a crossroad itself, but of course the way has at least two nodes, so this last node has already been written during the previous step and can safely be skipped.
				fprintf(fmif,"PLINE %i\n",i);
				while (n!=cross) {
					fprintf(fmif,"%f %f\n",n->node->x,n->node->y);
					n=n->next;
				}
				if (n!=0) fprintf(fmif,"%f %f\n",n->node->x,n->node->y);
				fprintf(fmid,"\"%li\"",id++);
				for (j=0;j<descr->number;++j) fprintf(fmid,",\"%s\"",(cur->way->attributes[j])?(cur->way->attributes[j]):"");
				fprintf(fmid,"\n");
			} else n=0;
		}
		cur=cur->next;
	}
	fclose(fmid);
	fclose(fmif);
}

/**
 * \brief Create MIF/MID files from an XML file describing a road network
 *
 * The function reads an XML file downloaded from OpenStreetMap which descriptor is given in argument, parses the XML code and creates two groups of MIF/MID files: one describing the "nodes" of the road network, which are the crossroads, and the other one for the roads themselves. Only the fields specified in the command line are exported (see also function main() for more information about command line arguments).
 * \param fd Descriptor of the file to parse. The file must be opened, and the file pointer should be at the beginning of the file. Furthermore, the user must of course have reading rights on it.
 * \param mi_file "Root name" of the MIF/MID files in which results will be written. Four files will be created :
 * 	- <tt>mi_file_way.mif</tt> : description of the geometry of linear elements (roads)
 * 	- <tt>mi_file_way.mid</tt> : description of the attributes of linear elements
 * 	- <tt>mi_file_node.mif</tt> : description of the geometry of ponctual elements (crossroads)
 * 	- <tt>mi_file_node.mid</tt> : description of the attributes of ponctual elements
 */
void export_osm(int fd,char *mi_file) {
	// Parse XML file
	LIBXML_TEST_VERSION
	xmlDocPtr doc;
	doc=xmlReadFd(fd,NULL,NULL,0);
	if (doc==0) {
		fprintf(stderr,"Can not parse XML file\n");
		return;
	}
	xmlNode *root=xmlDocGetRootElement(doc);
	// Read nodes and ways elements
	Nodes *nodes=0;
	Ways *ways=0;
	Description *node_description=(Description*)malloc(sizeof(Description));
	Description *way_description=(Description*)malloc(sizeof(Description));
	node_description->number=0;
	way_description->number=0;
	xmlNode *cur;
	double lat,lon;
	for (cur=root->children;cur;cur=cur->next) {
		if (cur->type==XML_ELEMENT_NODE && strcmp((char*)(cur->name),"node")==0) {
			Node *node=(Node*)malloc(sizeof(Node));
			int i;
			for (i=0;i<MAX_ATTRIBUTES;++i) node->attributes[i]=0;
			node->description=node_description;
			node->number=0;
			xmlAttr *prop=cur->properties;
			while (prop!=0) {
				if (strcmp((char*)(prop->name),"id")==0) node->id=atoi((char*)(prop->children->content));
				else if (strcmp((char*)(prop->name),"lat")==0) lat=atof((char*)(prop->children->content));
				else if (strcmp((char*)(prop->name),"lon")==0) lon=atof((char*)(prop->children->content));
//				projection(lon*M_PI/180,lat*M_PI/180,&(node->x),&(node->y));
				node->x=lon;
				node->y=lat;
				prop=prop->next;
			}
			xmlNode *attributes=cur->children;
			while (attributes!=0) {
				if (attributes->type==XML_ELEMENT_NODE) {
					Field *f=0;
					if (strcmp((char*)(attributes->properties->name),"k")==0) f=insert_unique(node_description,(char*)(attributes->properties->children->content));
					if (f!=0 && strcmp((char*)(attributes->properties->next->name),"v")==0) {
						int size=strlen((char*)(attributes->properties->next->children->content))+1;
						if (node->attributes[f->position]==0) {
							node->attributes[f->position]=(char*)malloc(size);
							memcpy(node->attributes[f->position],attributes->properties->next->children->content,size);
						}
					}
				}
				attributes=attributes->next;
			}
			Nodes *new_nodes=(Nodes*)malloc(sizeof(Nodes));
			new_nodes->node=node;
			new_nodes->next=nodes;
			nodes=new_nodes;
		}
	}
	Node **snodes;
	int nb_nodes=sort_nodes(nodes,&snodes);
	for (cur=root->children;cur;cur=cur->next) {
		if (cur->type==XML_ELEMENT_NODE && strcmp((char*)(cur->name),"way")==0) {
			Way *way=(Way*)malloc(sizeof(Way));
			int i;
			for (i=0;i<MAX_ATTRIBUTES;++i) way->attributes[i]=0;
			way->description=way_description;
			way->nodes=0;
			way->nb_nodes=0;
			Nodes *curnode=0;
			xmlAttr *prop=cur->properties;
			while (prop!=0) {
				if (strcmp((char*)(prop->name),"id")==0) way->id=atoi((char*)(prop->children->content));
				prop=prop->next;
			}
			xmlNode *attributes=cur->children;
			while (attributes!=0) {
				if (attributes->type==XML_ELEMENT_NODE) {
					if (strcmp((char*)(attributes->name),"nd")==0) {
						Node *fn=find_node(snodes,nb_nodes,atoi((char*)(attributes->properties->children->content)));
						if (fn!=0) {
							Nodes *new_nodes=(Nodes*)malloc(sizeof(Nodes));
							new_nodes->node=fn;
							new_nodes->next=0;
							if (curnode==0) way->nodes=new_nodes; else curnode->next=new_nodes;
							curnode=new_nodes;
							fn->number++;
							way->nb_nodes++;
						}	
					} else if (strcmp((char*)(attributes->name),"tag")==0) {
						Field *f=0;
						if (strcmp((char*)(attributes->properties->name),"k")==0) f=insert_unique(way_description,(char*)(attributes->properties->children->content));
						if (f!=0 && strcmp((char*)(attributes->properties->next->name),"v")==0) {
							int size=strlen((char*)(attributes->properties->next->children->content))+1;
							if (way->attributes[f->position]==0) {
								way->attributes[f->position]=(char*)malloc(size);
								memcpy(way->attributes[f->position],attributes->properties->next->children->content,size);
							}
						}
					}
				}
				attributes=attributes->next;
			}
			Ways *new_ways=(Ways*)malloc(sizeof(Ways));
			new_ways->way=way;
			new_ways->next=ways;
			ways=new_ways;
		}
	}
	// Export to disk
	save_nodes(mi_file,snodes,nb_nodes);
	save_ways(mi_file,ways);
	// Free memory
	int i;
	free_description(node_description);
	free_description(way_description);
	for (i=0;i<nb_nodes;++i) free_node(snodes[i]);
	free(snodes);
	Ways *curw;
	for (curw=ways;curw;curw=curw->next) free_way(curw->way);
	free_ways(ways);
	xmlFreeDoc(doc);
	xmlCleanupParser();
}

/**
 * \brief Import topographic data from Nasa Shuttle Radar Topography Mission (SRTM).
 *
 * This functions connects to the Internet to download topographic data for the chosen region. It uses the 4th version of the NASA SRTM database (Nasa Shuttle radar topography mission), which is distributed by CGIAR. It downloads as many parcel files as necessary so that the whole region is covered, and automatically extracts the region from all the files, merging data in one output file. The longitude and latitude coordinates are relative to the WGS84 geodetic system.
 * \param fd Descriptor of the already opened file in which the content shall be written
 * \param west Minimum longitude in degrees
 * \param east Maximum longitude in degrees
 * \param south Minimum latitude in degrees
 * \param north Maximum latitude in degrees
 * \return 0 if the function ended normally, -1 otherwise
 */
int import_dem(int fd,double west,double east,double south,double north) {
	
}

/**
 * \brief Main program
 *
 * This is the main program implementation. The function parses parameters from the command line and does the actual work. If an error occurs, a message is printed on standard error.
 * Usage : program_name [option [option...]]
 * Options : -b min_longitude,min_latitude,max_longitude,max_latitude
 * 					 --boundaries min_longitude,min_latitude,max_longitude,max_latitude
 * 					 					Defines geographical boundaries of the maps to import. These boundaries must be given in degrees.
 * 					 -f filename
 * 					 --filename filename
 * 										Defines the name of the MIF/MID file in which data must be exported. The name must not include the MIF or MID extension, nor the dot separator. It may be a full absolute path or a relative path.
 * 					 -p server:port
 * 					 --proxy server:port
 * 					 					Defines the address and port number of the proxy server to use, if any
 * \param argc Number of parameters of the command line, including the name of the program itself
 * \param argv Array of parameters, the first one being the name by which the program has been called
 */
int main(int argc,char **argv) {
	int next_option;
	const char* const short_options="b:f:p:r::d";
	const struct option long_options[]={
		{"boundaries",1,0,'b'},	// Define the boundaries of the region to export
		{"filename",1,0,'f'},	// Name of the file to create or load
		{"proxy",1,0,'p'},	// Name of the proxy to use in the following format: "proxy.domain:port"
		{"roads",2,0,'r'},	// Download roads network from the OpenStreetMap website if no parameter is found, otherwise loads it from the file which name is given as the parameter of this command
		{"dem",0,0,'d'}	// Download digital elevation model from the NASA SRTM website
	};
	char *filename;
	char *pos;
	int fd;
	double north,east,south,west;
	do {
		next_option=getopt_long(argc,argv,short_options,long_options,0);
		switch (next_option) {
			case 'b':
				pos=strchr(optarg,',');
				int err=0;
				char buf[30];
				char *pos2;
				if (pos==0) err=1; else {
					strncpy(buf,optarg,pos-optarg);
					west=atof(buf);
				}
				if (err==0) {
					pos=pos+1;
					pos2=strchr(pos,',');
					if (pos2==0) err=1; else {
						strncpy(buf,pos,pos2-pos);
						south=atof(buf);
					}
				}
				if (err==0) {
					pos=pos2+1;
					pos2=strchr(pos,',');
					if (pos2==0) err=1; else {
						strncpy(buf,pos,pos2-pos);
						east=atof(buf);
						north=atof(pos2+1);
					}
				}
				break;
			case 'f':
				filename=optarg;
				break;
			case 'p':
				pos=strchr(optarg,':');
				if (pos==0) {
					strcpy(proxy,optarg);
					proxy_port=HTTP_PORT;
				} else {
					strncpy(proxy,optarg,pos-optarg);
					proxy_port=atoi(pos+1);
				}
				break;
			case 'r':
				if (optarg==0) {
					fd=fileno(tmpfile());
					if (import_osm(fd,west,east,south,north)==-1) fd=-1; else lseek(fd,0,SEEK_SET);
				} else {
					fd=open(optarg,O_RDONLY);
				}
				if (fd==-1) break;
				if (filename==0) export_osm(fd,"out"); else export_osm(fd,filename);
				close(fd);
				break;
			case '?':
				return EX_USAGE;
				break;
		}
	} while (next_option!=-1);
//	int f=creat("test3",0600);
//	unsigned long size=import_osm(f,2.7466,2.8943,49.3717,49.4484);
//	close(f);
//	if (size>=0) return 0; else return 1;
//	export_mif_mid("test2","sortie");
	return 0;
}
